package com.example.schedule.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.common.constants.ScheduleConstants;
import com.example.common.redis.RedisCache;
import com.example.model.schedule.dtos.Task;
import com.example.model.schedule.pojos.Taskinfo;
import com.example.model.schedule.pojos.TaskinfoLogs;
import com.example.schedule.mapper.TaskinfoLogsMapper;
import com.example.schedule.mapper.TaskinfoMapper;
import com.example.schedule.service.TaskService;
import com.huaban.analysis.jieba.JiebaSegmenter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Transactional
@Slf4j
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskinfoMapper taskinfoMapper;

    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;

    @Autowired
    private RedisCache redisCache;

//    @Autowired
//    private JiebaSegmenter jiebaSegmenter;

    @Override
    public long addTask(Task task) {

        log.info("public long addTask(Task task)");

        //添加到数据库
        boolean success = addTaskToDB(task);

        //添加到redis
        if(success){
            addTaskToCache(task);
        }
        return task.getTaskId();
    }

    @Override
    public boolean cancelTask(long taskId) {
        boolean flag = false;
        //删除任务，更新任务日志
        Task task = updateDB(taskId,ScheduleConstants.CANCELLED);
        System.out.println("task: "+task.toString());
        if(task != null){
            removeTaskFromCache(task);
            flag = true;
        }
        return flag;
    }

    @Override
    public Task pull(int type, int priority) {

        Task task = null;
        try {
            //redis中拉取数据
            String key = type + "_" + priority;
            String json = redisCache.lRightPop(ScheduleConstants.TOPIC + key);
            if (StringUtils.isNotBlank(json)) {
                task = JSON.parseObject(json, Task.class);
                //修改数据库信息
                updateDB(task.getTaskId(), ScheduleConstants.EXECUTED);
            }
        } catch (Exception e) {
            log.error("pull task exception");
            e.printStackTrace();
        }


        return task;
    }

    private void removeTaskFromCache(Task task) {
        //删除redis中的数据
        String key = task.getTaskType()+"_"+task.getPriority();
        if(task.getExecuteTime() <= System.currentTimeMillis()){
            redisCache.lRemove(ScheduleConstants.TOPIC+key,0,JSON.toJSONString(task));
        }else{
            System.out.println("key "+ScheduleConstants.FUTURE+key);
            redisCache.zRemove(ScheduleConstants.FUTURE+key,JSON.toJSONString(task));
        }
    }

    private Task updateDB(long taskId, int status) {
        Task task = null;
        try {
            //删除任务
            taskinfoMapper.deleteById(taskId);

            System.out.println("taskId "+taskId);

            //更新任务日志
            TaskinfoLogs taskinfoLogs = taskinfoLogsMapper.selectById(taskId);
            taskinfoLogs.setStatus(status);
            taskinfoLogsMapper.updateById(taskinfoLogs);

            task = new Task();
            BeanUtils.copyProperties(taskinfoLogs, task);
            task.setExecuteTime(taskinfoLogs.getExecuteTime().getTime());
        } catch (Exception e) {
            log.error("task cancel exception taskId={}", taskId);
            e.printStackTrace();
        }

        return task;
    }

    private void addTaskToCache(Task task) {

        String key = task.getTaskType() + "_" + task.getPriority();

        //获取5分钟之后的时间  毫秒值
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, 5);
        long nextScheduleTime = calendar.getTimeInMillis();
        log.info("task.getExecuteTime() {}",task.getExecuteTime()+2000);
        log.info("System.currentTimeMillis() {}",System.currentTimeMillis());

        //2.1 如果任务的执行时间小于等于当前时间，存入list
        if (task.getExecuteTime() <= System.currentTimeMillis()) {
            redisCache.lLeftPush(ScheduleConstants.TOPIC + key, JSON.toJSONString(task));
        } else if (task.getExecuteTime() <= nextScheduleTime) {
            // 2500
            //2.2 如果任务的执行时间大于当前时间 && 小于等于预设时间（未来5分钟） 存入zset中
            redisCache.zAdd(ScheduleConstants.FUTURE + key, JSON.toJSONString(task), task.getExecuteTime());
        }

//        String key = task.getTaskType()+"_"+task.getPriority();
//
//        Calendar calendar = Calendar.getInstance();
//        calendar.add(Calendar.MINUTE,5);
//        long timeMillis = calendar.getTimeInMillis();
//
//        //如果任务执行时间小于当前时间
//        if(task.getExecuteTime() <= System.currentTimeMillis()){
//            redisCache.lLeftPush(ScheduleConstants.TOPIC+key, JSON.toJSONString(task));
//        }else if(task.getExecuteTime() <= timeMillis){
//            //如果任务执行时间小于等于预算时间 && 大于当前时间
//            redisCache.zAdd(ScheduleConstants.FUTURE+key,JSON.toJSONString(task),task.getExecuteTime());
//
//        }
    }

    private boolean addTaskToDB(Task task) {
        log.info("test "+task.toString());
        boolean flag = false;
        try{
            //保存任务表
            Taskinfo taskinfo = new Taskinfo();
            BeanUtils.copyProperties(task,taskinfo);
            taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
            log.info("test taskinfo {}",taskinfo.toString());
            taskinfoMapper.insert(taskinfo);

            task.setTaskId(taskinfo.getTaskId());

            //保存日志数据
            TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
            BeanUtils.copyProperties(taskinfo,taskinfoLogs);
            taskinfoLogs.setVersion(1);
            taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED); // 初始化
            log.info("test taskinfoLogs {}",taskinfoLogs.toString());
            taskinfoLogsMapper.insert(taskinfoLogs);
            flag = true;
        }catch (Exception e){
            e.printStackTrace();
        }

        return flag;

    }

    /**
     * 未来数据定时刷新（每分钟）
     */
    @Scheduled(cron = "0 */1 * * * ?")
    public void refresh(){
        log.info("未来任务定时刷新");
        String token = redisCache.tryLock("FUTURE_TASK_SYNC", 1000 * 30);

        if(StringUtils.isNotBlank(token)){
            //获取keys
            Set<String> keys = redisCache.scan(ScheduleConstants.FUTURE+"*");
            for (String futureKey : keys) {
                Set<String> tasks = redisCache.zRangeByScore(futureKey, 0, System.currentTimeMillis()); //future_100_50

                //获取topicKey
                String topicKey = ScheduleConstants.TOPIC+futureKey.split(ScheduleConstants.FUTURE)[1];
                //同步数据
                if(!tasks.isEmpty()){
                    redisCache.refreshWithPipeline(futureKey,topicKey,tasks);
                    log.info("成功将"+futureKey+"刷新到了"+topicKey);
                }
            }
        }

    }

    /**
     * 数据库任务同步到redis中
     */
    @Scheduled(cron = "0 */5 * * * ?")
    @PostConstruct   //服务启动就执行
    public void reloadData(){
        //清理缓存中的数据
        clearCache();

        //查询符合条件的任务  小于5分钟的数据
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,5);
        List<Taskinfo> taskinfoList = taskinfoMapper.selectList(Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, calendar.getTime()));

        //存入redis
        if(taskinfoList != null && taskinfoList.size() > 0){
            for (Taskinfo taskinfo : taskinfoList){
                Task task = new Task();
                BeanUtils.copyProperties(taskinfo,task);
                task.setExecuteTime(taskinfo.getExecuteTime().getTime());
                addTaskToCache(task);
            }
        }

        log.info("数据库任务同步redis成功");
    }

    public void clearCache(){
        Set<String> topicKeys = redisCache.scan(ScheduleConstants.TOPIC + "*");
        Set<String> futureKeys = redisCache.scan(ScheduleConstants.FUTURE + "*");
        redisCache.delete(futureKeys);
        redisCache.delete(topicKeys);
    }
}
